home *** CD-ROM | disk | FTP | other *** search
Oberon Text | 1994-07-11 | 27.3 KB | 873 lines | [.Ob./.Ob4] |
- Syntax10.Scn.Fnt
- MODULE CRT; (* Cocol-R Tables *)
- IMPORT Texts, Oberon, Sets;
- CONST
- maxSymbols* = 300; (*max nr of t, nt, and pragmas*)
- maxTerminals* = 256; (*max nr of terminals*)
- maxNt* = 128; (*max nr of nonterminals*)
- maxNodes* = 1500; (*max nr of graph nodes*)
- normTrans* = 0; contextTrans* = 1; (*transition codes*)
- maxSetNr = 128; (* max. number of symbol sets *)
- maxClasses = 50; (* max. number of character classes *)
- (* node types *)
- t* = 1; pr* = 2; nt* = 3; class* = 4; char* = 5; wt* = 6; any* = 7; eps* = 8; sync* = 9; sem* = 10;
- alt* = 11; iter* = 12; opt* = 13;
- noSym* = -1;
- eofSy* = 0;
- (* token kinds *)
- classToken* = 0; (*token class*)
- litToken* = 1; (*literal (e.g. keyword) not recognized by DFA*)
- classLitToken* = 2; (*token class that can also match a literal*)
- Name* = ARRAY 16 OF CHAR; (*symbol name*)
- Position* = RECORD (*position of stretch of source text*)
- beg*: LONGINT; (*start relative to beginning of file*)
- len*: INTEGER; (*length*)
- col*: INTEGER; (*column number of start position*)
- END;
- SymbolNode* = RECORD
- typ*: INTEGER; (*nt, t, pr, unknown*)
- name*: Name; (*symbol name*)
- struct*: INTEGER; (*typ = nt: index of 1st node of syntax graph*)
- (*typ = t: token kind: literal, class, ...*)
- deletable*: BOOLEAN; (*typ = nt: TRUE, if nonterminal is deletable*)
- attrPos*: Position; (*position of attributes in source text*)
- semPos*: Position; (*typ = pr: pos of sem action in source text*)
- (*typ = nt: pos of local decls in source text *)
- line*: INTEGER; (*source text line number of item in this node*)
- END;
- Set* = ARRAY maxTerminals DIV Sets.size OF SET;
- GraphNode* = RECORD
- typ* : INTEGER; (* nt,sts,wts,char,class,any,eps,sem,sync,alt,iter,opt*)
- next*: INTEGER; (* index of successor node *)
- (* next < 0: to successor in enclosing structure *)
- p1*: INTEGER; (* typ IN {nt, t, wt}: index to symbol list *)
- (* typ = any: index to anyset *)
- (* typ = sync: index to syncset *)
- (* typ = alt: index of 1st node of 1st alternative*)
- (* typ IN {iter, opt}: 1st node in subexpression *)
- (* typ = char: ordinal character value *)
- (* typ = class: index of character class *)
- p2*: INTEGER; (* typ = alt: index of 1st node of 2nd alternative*)
- (* typ IN {char, class}: transition code *)
- pos*: Position; (* typ IN {nt, t, wt}: pos of actual attribs *)
- (* typ = sem: pos of sem action in source text. *)
- line*: INTEGER; (* source text line number of item in this node *)
- END;
- MarkList* = ARRAY maxNodes DIV Sets.size OF SET;
- FirstSets = ARRAY maxNt OF RECORD
- ts: Set; (*terminal symbols*)
- ready: BOOLEAN; (*TRUE = ts is complete*)
- END;
- FollowSets = ARRAY maxNt OF RECORD
- ts: Set; (*terminal symbols*)
- nts: Set; (*nts whose start set is to be included*)
- END;
- CharClass = RECORD
- name: Name; (*class name*)
- set: INTEGER (* ptr to set representing the class*)
- END;
- SymbolTable = ARRAY maxSymbols OF SymbolNode;
- ClassTable = ARRAY maxClasses OF CharClass;
- GraphList = ARRAY maxNodes OF GraphNode;
- maxSet*: INTEGER; (* index of last set *)
- maxT*: INTEGER; (* terminals stored from 0 .. maxT *)
- maxP*: INTEGER; (* pragmas stored from maxT+1 .. maxP *)
- firstNt*: INTEGER; (* index of first nt: available after CompSymbolSets *)
- lastNt*: INTEGER; (* index of last nt: available after CompSymbolSets *)
- maxC*: INTEGER; (* index of last character class *)
- semDeclPos*: Position; (*position of global semantic declarations*)
- importPos*: Position; (*position of imported identifiers*)
- ignored*: Set; (* characters ignored by the scanner *)
- ignoreCase*: BOOLEAN; (* TRUE: scanner treats lower case as upper case*)
- ddt*: ARRAY 10 OF BOOLEAN; (* debug and test switches *)
- nNodes*: INTEGER; (* index of last graph node *)
- root*: INTEGER; (* index of root node, filled by ATG *)
- w: Texts.Writer;
- st: SymbolTable;
- gn: GraphList;
- first: FirstSets; (*first[i] = first symbols of st[i+firstNt]*)
- follow: FollowSets; (*follow[i] = followers of st[i+firstNt]*)
- chClass: ClassTable; (*character classes*)
- set: ARRAY 128 OF Set; (*set[0] reserved for union of all synchronisation sets*)
- dummyName: INTEGER; (*for unnamed character classes*)
- PROCEDURE ^MovePragmas;
- PROCEDURE ^DelNode*(gn: GraphNode): BOOLEAN;
- PROCEDURE Str(s: ARRAY OF CHAR);
- BEGIN Texts.WriteString(w, s)
- END Str;
- PROCEDURE NL;
- BEGIN Texts.WriteLn(w)
- END NL;
- PROCEDURE Length(s: ARRAY OF CHAR): INTEGER;
- VAR i: INTEGER;
- BEGIN
- i:=0; WHILE (i < LEN(s)) & (s[i] # 0X) DO INC(i) END;
- RETURN i
- END Length;
- PROCEDURE Restriction(n: INTEGER);
- BEGIN
- NL; Str("Restriction "); Texts.WriteInt(w, n, 0); NL; Texts.Append(Oberon.Log, w.buf);
- HALT(99)
- END Restriction;
- PROCEDURE ClearMarkList(VAR m: MarkList);
- VAR i: INTEGER;
- BEGIN
- i := 0; WHILE i < maxNodes DIV Sets.size DO m[i] := {}; INC(i) END;
- END ClearMarkList;
- PROCEDURE GetNode*(gp: INTEGER; VAR n: GraphNode);
- BEGIN
- n := gn[gp]
- END GetNode;
- PROCEDURE PutNode*(gp: INTEGER; n: GraphNode);
- BEGIN gn[gp] := n
- END PutNode;
- PROCEDURE DelGraph*(gp: INTEGER): BOOLEAN;
- VAR gn: GraphNode;
- BEGIN
- IF gp = 0 THEN RETURN TRUE END; (*end of graph found*)
- GetNode(gp, gn);
- RETURN DelNode(gn) & DelGraph(ABS(gn.next));
- END DelGraph;
- PROCEDURE NewSym*(typ: INTEGER; name: Name; line: INTEGER): INTEGER;
- VAR i: INTEGER;
- BEGIN
- IF maxT + 1 = firstNt THEN Restriction(6)
- ELSE
- CASE typ OF
- | t: INC(maxT); i := maxT
- | pr: DEC(maxP); DEC(firstNt); DEC(lastNt); i := maxP
- | nt: DEC(firstNt); i := firstNt
- END;
- IF maxT >= maxTerminals THEN Restriction(6) END;
- st[i].typ := typ; st[i].name := name;
- st[i].struct := 0; st[i].deletable := FALSE;
- st[i].attrPos.beg := -1;
- st[i].semPos.beg := -1;
- st[i].line := line
- END;
- RETURN i
- END NewSym;
- PROCEDURE GetSym*(sp: INTEGER; VAR sn: SymbolNode);
- BEGIN sn := st[sp]
- END GetSym;
- PROCEDURE PutSym*(sp: INTEGER; sn: SymbolNode);
- BEGIN st[sp] := sn
- END PutSym;
- PROCEDURE FindSym*(name: Name): INTEGER;
- VAR i: INTEGER;
- BEGIN
- i := 0; (*search in terminal list*)
- WHILE (i <= maxT) & (st[i].name # name) DO INC(i) END;
- IF i <= maxT THEN RETURN i END;
- i := firstNt; (*search in nonterminal/pragma list*)
- WHILE (i < maxSymbols) & (st[i].name # name) DO INC(i) END;
- IF i < maxSymbols THEN RETURN i ELSE RETURN noSym END
- END FindSym;
- PROCEDURE NewSet*(s: Set): INTEGER;
- BEGIN
- INC(maxSet); IF maxSet > maxSetNr THEN Restriction(4) END;
- set[maxSet] := s;
- RETURN maxSet
- END NewSet;
- PROCEDURE PrintSet(s: ARRAY OF SET; indent: INTEGER);
- CONST maxLineLen = 80;
- VAR col, i, len: INTEGER; empty: BOOLEAN; sn: SymbolNode;
- BEGIN
- i := 0; col := indent; empty := TRUE;
- WHILE i <= maxT DO
- IF Sets.In(s, i) THEN
- empty := FALSE; GetSym(i, sn); len := Length(sn.name);
- IF col + len + 2 > maxLineLen THEN
- NL; col := 1;
- WHILE col < indent DO Texts.Write(w, " "); INC(col) END
- END;
- Str(sn.name); Str(" ");
- INC(col, len + 2)
- END;
- INC(i)
- END;
- IF empty THEN Str("-- empty set --") END;
- NL; Texts.Append(Oberon.Log, w.buf)
- END PrintSet;
- PROCEDURE CompFirstSet*(gp: INTEGER; VAR fs: Set);
- VAR visited: MarkList;
- PROCEDURE CompFirst(gp: INTEGER; VAR fs: Set);
- VAR s: Set; gn: GraphNode; sn: SymbolNode;
- BEGIN
- Sets.Clear(fs);
- WHILE (gp # 0) & ~ Sets.In(visited, gp) DO
- GetNode(gp, gn); Sets.Incl(visited, gp);
- CASE gn.typ OF
- | nt:
- IF first[gn.p1 - firstNt].ready THEN
- Sets.Unite(fs, first[gn.p1 - firstNt].ts);
- ELSE
- GetSym(gn.p1, sn); CompFirst(sn.struct, s); Sets.Unite(fs, s);
- END;
- | t, wt: Sets.Incl(fs, gn.p1);
- | any: Sets.Unite(fs, set[gn.p1])
- | alt, iter, opt:
- CompFirst(gn.p1, s); Sets.Unite(fs, s);
- IF gn.typ = alt THEN CompFirst(gn.p2, s); Sets.Unite(fs, s) END
- ELSE (* eps, sem, sync: nothing *)
- END;
- IF ~ DelNode(gn) THEN RETURN END;
- gp := ABS(gn.next)
- END
- END CompFirst;
- BEGIN (* ComputeFirstSet *)
- ClearMarkList(visited);
- CompFirst(gp, fs);
- IF ddt[3] THEN
- NL; Str("ComputeFirstSet: gp = "); Texts.WriteInt(w, gp, 0); NL;
- PrintSet(fs, 0);
- END;
- END CompFirstSet;
- PROCEDURE CompFirstSets;
- VAR i: INTEGER; sn: SymbolNode;
- BEGIN
- i := firstNt; WHILE i <= lastNt DO first[i-firstNt].ready := FALSE; INC(i) END;
- i := firstNt;
- WHILE i <= lastNt DO (* for all nonterminals *)
- GetSym(i, sn); CompFirstSet(sn.struct, first[i - firstNt].ts);
- first[i - firstNt].ready := TRUE;
- INC(i)
- END;
- END CompFirstSets;
- PROCEDURE CompExpected*(gp, sp: INTEGER; VAR exp: Set);
- BEGIN
- CompFirstSet(gp, exp);
- IF DelGraph(gp) THEN Sets.Unite(exp, follow[sp - firstNt].ts) END
- END CompExpected;
- PROCEDURE CompFollowSets;
- VAR sn: SymbolNode; gn: GraphNode; curSy, i, size: INTEGER; visited: MarkList;
- PROCEDURE CompFol(gp: INTEGER);
- VAR s: Set; gn: GraphNode;
- BEGIN
- WHILE (gp > 0) & ~ Sets.In(visited, gp) DO
- GetNode(gp, gn); Sets.Incl(visited, gp);
- IF gn.typ = nt THEN
- CompFirstSet(ABS(gn.next), s); Sets.Unite(follow[gn.p1 - firstNt].ts, s);
- IF DelGraph(ABS(gn.next)) THEN
- Sets.Incl(follow[gn.p1 - firstNt].nts, curSy - firstNt)
- END
- ELSIF gn.typ IN {opt, iter} THEN CompFol(gn.p1)
- ELSIF gn.typ = alt THEN CompFol(gn.p1); CompFol(gn.p2)
- END;
- gp := gn.next
- END
- END CompFol;
- PROCEDURE Complete(i: INTEGER);
- VAR j: INTEGER;
- BEGIN
- IF Sets.In(visited, i) THEN RETURN END;
- Sets.Incl(visited, i);
- j := 0;
- WHILE j <= lastNt - firstNt DO (* for all nonterminals *)
- IF Sets.In(follow[i].nts, j) THEN
- Complete(j); Sets.Unite(follow[i].ts, follow[j].ts);
- Sets.Excl(follow[i].nts, j)
- END;
- INC(j)
- END;
- END Complete;
- BEGIN (* CompFollowSets *)
- curSy := firstNt; size := (lastNt - firstNt + 2) DIV Sets.size;
- WHILE curSy <= lastNt + 1 DO (* also for dummy root nt*)
- Sets.Clear(follow[curSy - firstNt].ts);
- i := 0; WHILE i <= size DO follow[curSy - firstNt].nts[i] := {}; INC(i) END;
- INC(curSy)
- END;
- curSy := firstNt; (*get direct successors of nonterminals*)
- WHILE curSy <= lastNt DO
- GetSym(curSy, sn); ClearMarkList(visited); CompFol(sn.struct);
- INC(curSy)
- END;
- CompFol(root); (*curSy=lastNt+1*)
- curSy := 0; (*add indirect successors to follow.ts*)
- WHILE curSy <= lastNt - firstNt DO
- ClearMarkList(visited); Complete(curSy);
- INC(curSy);
- END;
- END CompFollowSets;
- PROCEDURE CompAnySets;
- VAR curSy, i: INTEGER; sn: SymbolNode;
- PROCEDURE LeadingAny(gp: INTEGER; VAR a: GraphNode): BOOLEAN;
- VAR gn: GraphNode;
- BEGIN
- IF gp <= 0 THEN RETURN FALSE END;
- GetNode(gp, gn);
- IF (gn.typ = any) THEN a := gn; RETURN TRUE
- ELSE RETURN (gn.typ = alt) & (LeadingAny(gn.p1, a) OR LeadingAny(gn.p2, a))
- OR (gn.typ IN {opt, iter}) & LeadingAny(gn.p1, a)
- OR DelNode(gn) & LeadingAny(gn.next, a)
- END
- END LeadingAny;
- PROCEDURE FindAS(gp: INTEGER);
- VAR gn, gn2, a: GraphNode; s1, s2: Set; p: INTEGER;
- BEGIN
- WHILE gp > 0 DO
- GetNode(gp, gn);
- IF gn.typ IN {opt, iter} THEN
- FindAS(gn.p1);
- IF LeadingAny(gn.p1, a) THEN
- CompFirstSet(ABS(gn.next), s1); Sets.Differ(set[a.p1], s1)
- END
- ELSIF gn.typ = alt THEN
- p := gp; Sets.Clear(s1);
- WHILE p # 0 DO
- GetNode(p, gn2); FindAS(gn2.p1);
- IF LeadingAny(gn2.p1, a) THEN
- CompFirstSet(gn2.p2, s2); Sets.Unite(s2, s1); Sets.Differ(set[a.p1], s2)
- ELSE
- CompFirstSet(gn2.p1, s2); Sets.Unite(s1, s2)
- END;
- p := gn2.p2
- END
- END;
- gp := gn.next
- END
- END FindAS;
- BEGIN
- curSy := firstNt;
- WHILE curSy <= lastNt DO (* for all nonterminals *)
- GetSym(curSy, sn); FindAS(sn.struct);
- INC(curSy)
- END CompAnySets;
- PROCEDURE CompSyncSets;
- VAR curSy, i: INTEGER; sn: SymbolNode; visited: MarkList;
- PROCEDURE CompSync(gp: INTEGER);
- VAR s: Set; gn: GraphNode;
- BEGIN
- WHILE (gp > 0) & ~ Sets.In(visited, gp) DO
- GetNode(gp, gn); Sets.Incl(visited, gp);
- IF gn.typ = sync THEN
- CompExpected(ABS(gn.next), curSy, s);
- Sets.Incl(s, eofSy); Sets.Unite(set[0], s);
- gn.p1 := NewSet(s); PutNode(gp, gn)
- ELSIF gn.typ = alt THEN CompSync(gn.p1); CompSync(gn.p2)
- ELSIF gn.typ IN {iter, opt} THEN CompSync(gn.p1)
- END;
- gp := gn.next
- END
- END CompSync;
- BEGIN
- curSy := firstNt; ClearMarkList(visited);
- WHILE curSy <= lastNt DO
- GetSym(curSy, sn); CompSync(sn.struct);
- INC(curSy);
- END CompSyncSets;
- PROCEDURE CompDeletableSymbols*;
- VAR changed, del: BOOLEAN; i: INTEGER; sn: SymbolNode;
- BEGIN
- del := FALSE;
- REPEAT
- changed := FALSE;
- i := firstNt;
- WHILE i <= lastNt DO (*for all nonterminals*)
- GetSym(i, sn);
- IF ~sn.deletable & DelGraph(sn.struct) THEN
- sn.deletable := TRUE; PutSym(i, sn); changed := TRUE; del := TRUE
- END;
- INC(i)
- END;
- UNTIL ~changed;
- i := firstNt; IF del THEN NL END;
- WHILE i <= lastNt DO
- GetSym(i, sn);
- IF sn.deletable THEN Str(" "); Str(sn.name); Str(" deletable"); NL END;
- INC(i);
- END;
- Texts.Append(Oberon.Log, w.buf)
- END CompDeletableSymbols;
- PROCEDURE CompSymbolSets*;
- VAR i: INTEGER; sn: SymbolNode;
- BEGIN
- i := NewSym(t, "???", 0); (*unknown symbols get code maxT*)
- MovePragmas;
- CompDeletableSymbols;
- CompFirstSets;
- CompFollowSets;
- CompAnySets;
- CompSyncSets;
- IF ddt[1] THEN
- i := firstNt; Str("First & follow symbols:"); NL;
- WHILE i <= lastNt DO (* for all nonterminals *)
- GetSym(i, sn); Str(sn.name); NL;
- Str("first: "); PrintSet(first[i - firstNt].ts, 10);
- Str("follow: "); PrintSet(follow[i - firstNt].ts, 10);
- NL;
- INC(i);
- END;
- IF maxSet >= 0 THEN NL; NL; Str("List of sets (ANY, SYNC): "); NL END;
- i := 0;
- WHILE i <= maxSet DO
- Str(" set["); Texts.WriteInt (w, i, 2); Str("] = "); PrintSet(set[i], 16);
- INC (i)
- END;
- NL; NL; Texts.Append(Oberon.Log, w.buf)
- END;
- END CompSymbolSets;
- PROCEDURE GetFirstSet(sp: INTEGER; VAR s: Set);
- BEGIN s := first[sp - firstNt].ts
- END GetFirstSet;
- PROCEDURE GetFollowSet(sp: INTEGER; VAR s: Set);
- BEGIN s := follow[sp - firstNt].ts
- END GetFollowSet;
- PROCEDURE GetSet*(nr: INTEGER; VAR s: Set);
- BEGIN s := set[nr]
- END GetSet;
- PROCEDURE MovePragmas;
- VAR i: INTEGER;
- BEGIN
- IF maxP > firstNt THEN
- i := maxSymbols - 1; maxP := maxT;
- WHILE i > lastNt DO
- INC(maxP); IF maxP >= firstNt THEN Restriction(6) END;
- st[maxP] := st[i]; DEC(i)
- END;
- END MovePragmas;
- PROCEDURE PrintSymbolTable*;
- VAR i, j: INTEGER;
- PROCEDURE WriteTyp(typ: INTEGER);
- BEGIN
- CASE typ OF
- | t : Str(" t ");
- | pr : Str(" pr ");
- | nt : Str(" nt ");
- END;
- END WriteTyp;
- BEGIN (* PrintSymbolTable *)
- Str("Symbol Table:"); NL; NL;
- Str("nr name typ hasAttribs struct del line"); NL; NL;
- i := 0;
- WHILE i < maxSymbols DO
- Texts.WriteInt(w, i, 3); Str(" ");
- j := 0; WHILE (j < 8) & (st[i].name[j] # 0X) DO Texts.Write(w, st[i].name[j]); INC(j) END;
- WHILE j < 8 DO Texts.Write(w, " "); INC(j) END;
- WriteTyp(st[i].typ);
- IF st[i].attrPos.beg >= 0 THEN Str(" TRUE ") ELSE Str(" FALSE") END;
- Texts.WriteInt(w, st[i].struct, 10);
- IF st[i].deletable THEN Str(" TRUE ") ELSE Str(" FALSE") END;
- Texts.WriteInt(w, st[i].line, 6); NL;
- IF i = maxT THEN i := firstNt ELSE INC(i) END
- END;
- NL; NL; Texts.Append(Oberon.Log, w.buf)
- END PrintSymbolTable;
- PROCEDURE NewClass*(name: Name; set: Set): INTEGER;
- BEGIN
- INC(maxC); IF maxC >= maxClasses THEN Restriction(7) END;
- IF name[0] = "#" THEN name[1] := CHR(ORD("A") + dummyName); INC(dummyName) END;
- chClass[maxC].name := name; chClass[maxC].set := NewSet(set);
- RETURN maxC
- END NewClass;
- PROCEDURE ClassWithName*(name: Name): INTEGER;
- VAR i: INTEGER;
- BEGIN
- i := maxC; WHILE (i >= 0) & (chClass[i].name # name) DO DEC(i) END;
- RETURN i
- END ClassWithName;
- PROCEDURE ClassWithSet*(s: Set): INTEGER;
- VAR i: INTEGER;
- BEGIN
- i := maxC; WHILE (i >= 0) & ~ Sets.Equal(set[chClass[i].set], s) DO DEC(i) END;
- RETURN i
- END ClassWithSet;
- PROCEDURE GetClass*(n: INTEGER; VAR s: Set);
- BEGIN
- GetSet(chClass[n].set, s)
- END GetClass;
- PROCEDURE GetClassName*(n: INTEGER; VAR name: Name);
- BEGIN
- name := chClass[n].name
- END GetClassName;
- PROCEDURE XRef*;
- CONST maxLineLen = 80;
- TYPE ListPtr = POINTER TO ListNode;
- ListNode = RECORD
- next: ListPtr;
- line: INTEGER;
- END;
- ListHdr = RECORD
- name: Name;
- lptr: ListPtr;
- END;
- VAR gn: GraphNode; col, i, j: INTEGER; l, p, q: ListPtr;
- sn: SymbolNode;
- xList: ARRAY maxSymbols OF ListHdr;
- BEGIN (* XRef *)
- IF maxT <= 0 THEN RETURN END;
- MovePragmas;
- (* initialise cross reference list *)
- i := 0;
- WHILE i <= lastNt DO (* for all symbols *)
- GetSym(i, sn); xList[i].name := sn.name; xList[i].lptr := NIL;
- IF i = maxP THEN i := firstNt ELSE INC(i) END
- END;
- (* search lines where symbol has been referenced *)
- i := 1;
- WHILE i <= nNodes DO (* for all graph nodes *)
- GetNode(i, gn);
- IF gn.typ IN {t, wt, nt} THEN
- NEW(l); l^.next := xList[gn.p1].lptr; l^.line := gn.line;
- xList[gn.p1].lptr := l
- END;
- INC(i);
- END;
- (* search lines where symbol has been defined and insert in order *)
- i := 1;
- WHILE i <= lastNt DO (*for all symbols*)
- GetSym(i, sn); p := xList[i].lptr; q := NIL;
- WHILE (p # NIL) & (p^.line > sn.line) DO q := p; p := p^.next END;
- NEW(l); l^.next := p;
- l^.line := -sn.line;
- IF q # NIL THEN q^.next := l ELSE xList[i].lptr := l END;
- IF i = maxP THEN i := firstNt ELSE INC(i) END
- END;
- (* print cross reference listing *)
- NL; Str("Cross reference list:"); NL; NL; Str("Terminals:"); NL; Str(" 0 EOF"); NL;
- i := 1;
- WHILE i <= lastNt DO (*for all symbols*)
- Texts.WriteInt(w, i, 3); Str(" ");
- j := 0; WHILE (j < 15) & (xList[i].name[j] # 0X) DO Texts.Write(w, xList[i].name[j]); INC(j) END;
- l := xList[i].lptr; col := 25;
- WHILE l # NIL DO
- IF col + 5 > maxLineLen THEN
- NL; col := 0; WHILE col < 25 DO Texts.Write(w, " "); INC(col) END
- END;
- IF l^.line = 0 THEN Str("undef") ELSE Texts.WriteInt(w, l^.line, 5) END;
- INC(col, 5);
- l := l^.next
- END;
- NL;
- IF i = maxT THEN NL; Str("Pragmas:"); NL END;
- IF i = maxP THEN NL; Str("Nonterminals:"); NL; i := firstNt ELSE INC(i) END
- END;
- NL; NL; Texts.Append(Oberon.Log, w.buf)
- END XRef;
- PROCEDURE NewNode*(typ, p1, line: INTEGER): INTEGER;
- BEGIN
- INC(nNodes); IF nNodes > maxNodes THEN Restriction(3) END;
- gn[nNodes].typ := typ; gn[nNodes].next := 0;
- gn[nNodes].p1 := p1; gn[nNodes].p2 := 0;
- gn[nNodes].pos.beg := -1; gn[nNodes].line := line;
- RETURN nNodes;
- END NewNode;
- PROCEDURE CompleteGraph*(gp: INTEGER);
- VAR p: INTEGER;
- BEGIN
- WHILE gp # 0 DO
- p := gn[gp].next; gn[gp].next := 0; gp := p
- END CompleteGraph;
- PROCEDURE ConcatAlt*(VAR gL1, gR1: INTEGER; gL2, gR2: INTEGER);
- VAR p: INTEGER;
- BEGIN
- gL2 := NewNode(alt, gL2, 0);
- p := gL1; WHILE gn[p].p2 # 0 DO p := gn[p].p2 END; gn[p].p2 := gL2;
- p := gR1; WHILE gn[p].next # 0 DO p := gn[p].next END; gn[p].next := gR2
- END ConcatAlt;
- PROCEDURE ConcatSeq*(VAR gL1, gR1: INTEGER; gL2, gR2: INTEGER);
- VAR p, q: INTEGER;
- BEGIN
- p := gn[gR1].next; gn[gR1].next := gL2; (*head node*)
- WHILE p # 0 DO (*substructure*)
- q := gn[p].next; gn[p].next := -gL2; p := q
- END;
- gR1 := gR2
- END ConcatSeq;
- PROCEDURE MakeFirstAlt*(VAR gL, gR: INTEGER);
- BEGIN
- gL := NewNode(alt, gL, 0); gn[gL].next := gR; gR := gL
- END MakeFirstAlt;
- PROCEDURE MakeIteration*(VAR gL, gR: INTEGER);
- VAR p, q: INTEGER;
- BEGIN
- gL := NewNode(iter, gL, 0); p := gR; gR := gL;
- WHILE p # 0 DO
- q := gn[p].next; gn[p].next := - gL; p := q
- END MakeIteration;
- PROCEDURE MakeOption*(VAR gL, gR: INTEGER);
- BEGIN
- gL := NewNode(opt, gL, 0); gn[gL].next := gR; gR := gL
- END MakeOption;
- PROCEDURE StrToGraph*(str: ARRAY OF CHAR; VAR gL, gR: INTEGER);
- VAR len, i: INTEGER;
- BEGIN
- gR := 0; i := 1; len := Length(str) - 1;
- WHILE i < len DO
- gn[gR].next := NewNode(char, ORD(str[i]), 0); gR := gn[gR].next;
- INC(i)
- END;
- gL := gn[0].next; gn[0].next := 0
- END StrToGraph;
- PROCEDURE DelNode*(gn: GraphNode): BOOLEAN;
- VAR sn: SymbolNode;
- PROCEDURE DelAlt(gp: INTEGER): BOOLEAN;
- VAR gn: GraphNode;
- BEGIN
- IF gp <= 0 THEN RETURN TRUE END; (*end of graph found*)
- GetNode(gp, gn);
- RETURN DelNode(gn) & DelAlt(gn.next);
- END DelAlt;
- BEGIN
- IF gn.typ = nt THEN GetSym(gn.p1, sn); RETURN sn.deletable
- ELSIF gn.typ = alt THEN RETURN DelAlt(gn.p1) OR (gn.p2 # 0) & DelAlt(gn.p2)
- ELSE RETURN gn.typ IN {eps, iter, opt, sem, sync}
- END DelNode;
- PROCEDURE PrintGraph*;
- VAR i: INTEGER;
- PROCEDURE WriteTyp(typ: INTEGER);
- BEGIN
- CASE typ OF
- | nt : Str("nt ")
- | t : Str("t ")
- | wt : Str("wt ")
- | any : Str("any ")
- | eps : Str("eps ")
- | sem : Str("sem ")
- | sync: Str("sync")
- | alt : Str("alt ")
- | iter: Str("iter")
- | opt : Str("opt ")
- ELSE Str("--- ")
- END;
- END WriteTyp;
- BEGIN (* PrintGraph *)
- Str("GraphList:"); NL; NL;
- Str(" nr typ next p1 p2 line"); NL; NL;
- i := 0;
- WHILE i <= nNodes DO
- Texts.WriteInt(w, i, 3); Str(" ");
- WriteTyp(gn[i].typ); Texts.WriteInt(w, gn[i].next, 7);
- Texts.WriteInt(w, gn[i].p1, 7);
- Texts.WriteInt(w, gn[i].p2, 7);
- Texts.WriteInt(w, gn[i].line, 7);
- NL;
- INC(i);
- END;
- NL; NL; Texts.Append(Oberon.Log, w.buf)
- END PrintGraph;
- PROCEDURE FindCircularProductions* (VAR ok: BOOLEAN);
- CONST maxList = 150;
- TYPE ListEntry = RECORD
- left : INTEGER;
- right : INTEGER;
- deleted: BOOLEAN;
- END;
- VAR changed, onLeftSide, onRightSide: BOOLEAN; i, j, listLength: INTEGER;
- list: ARRAY maxList OF ListEntry;
- singles: MarkList;
- sn: SymbolNode;
- PROCEDURE GetSingles (gp: INTEGER; VAR singles: MarkList);
- VAR gn: GraphNode;
- BEGIN
- IF gp <= 0 THEN RETURN END; (* end of graph found *)
- GetNode (gp, gn);
- IF gn.typ = nt THEN
- IF DelGraph(ABS(gn.next)) THEN Sets.Incl(singles, gn.p1) END
- ELSIF gn.typ IN {alt, iter, opt} THEN
- IF DelGraph(ABS(gn.next)) THEN
- GetSingles(gn.p1, singles);
- IF gn.typ = alt THEN GetSingles(gn.p2, singles) END
- END
- END;
- IF DelNode(gn) THEN GetSingles(gn.next, singles) END
- END GetSingles;
- BEGIN (* FindCircularProductions *)
- i := firstNt; listLength := 0;
- WHILE i <= lastNt DO (* for all nonterminals i *)
- ClearMarkList (singles); GetSym (i, sn);
- GetSingles (sn.struct, singles); (* get nt's j such that i-->j *)
- j := firstNt;
- WHILE j <= lastNt DO (* for all nonterminals j *)
- IF Sets.In(singles, j) THEN
- list[listLength].left := i; list[listLength].right := j;
- list[listLength].deleted := FALSE;
- INC (listLength)
- END;
- INC(j)
- END;
- INC(i)
- END;
- REPEAT
- i := 0; changed := FALSE;
- WHILE i < listLength DO
- IF ~ list[i].deleted THEN
- j := 0; onLeftSide := FALSE; onRightSide := FALSE;
- WHILE j < listLength DO
- IF ~ list[j].deleted THEN
- IF list[i].left = list[j].right THEN onRightSide := TRUE END;
- IF list[j].left = list[i].right THEN onLeftSide := TRUE END
- END;
- INC(j)
- END;
- IF ~ onRightSide OR ~ onLeftSide THEN
- list[i].deleted := TRUE; changed := TRUE
- END
- END;
- INC(i)
- END
- UNTIL ~ changed;
- i := 0; ok := TRUE;
- WHILE i < listLength DO
- IF ~ list[i].deleted THEN
- ok := FALSE;
- GetSym(list[i].left, sn); NL; Str(" "); Str(sn.name); Str(" --> ");
- GetSym(list[i].right, sn); Str(sn.name)
- END;
- INC(i)
- END;
- Texts.Append(Oberon.Log, w.buf)
- END FindCircularProductions;
- PROCEDURE LL1Test* (VAR ll1: BOOLEAN);
- VAR sn: SymbolNode; curSy: INTEGER;
- PROCEDURE LL1Error (cond, ts: INTEGER);
- VAR sn: SymbolNode;
- BEGIN
- ll1 := FALSE;
- GetSym (curSy, sn); Str(" LL1 error in "); Str(sn.name); Str(": ");
- IF ts > 0 THEN GetSym (ts, sn); Str(sn.name); Str(" is ") END;
- CASE cond OF
- 1: Str(" start of several alternatives.")
- | 2: Str(" start & successor of deletable structure")
- | 3: Str(" an ANY node that matchs no symbol")
- END;
- NL; Texts.Append(Oberon.Log, w.buf)
- END LL1Error;
- PROCEDURE Check (cond: INTEGER; VAR s1, s2: Set);
- VAR i: INTEGER;
- BEGIN
- i := 0;
- WHILE i <= maxT DO
- IF Sets.In(s1, i) & Sets.In(s2, i) THEN LL1Error(cond, i) END;
- INC(i)
- END
- END Check;
- PROCEDURE CheckAlternatives (gp: INTEGER);
- VAR gn, gn1: GraphNode; s1, s2: Set; p: INTEGER;
- BEGIN
- WHILE gp > 0 DO
- GetNode(gp, gn);
- IF gn.typ = alt THEN
- p := gp; Sets.Clear(s1);
- WHILE p # 0 DO (*for all alternatives*)
- GetNode(p, gn1); CompExpected(gn1.p1, curSy, s2);
- Check(1, s1, s2); Sets.Unite(s1, s2);
- CheckAlternatives(gn1.p1);
- p := gn1.p2
- END
- ELSIF gn.typ IN {opt, iter} THEN
- CompExpected(gn.p1, curSy, s1);
- CompExpected(ABS(gn.next), curSy, s2);
- Check(2, s1, s2);
- CheckAlternatives(gn.p1)
- ELSIF gn.typ = any THEN
- GetSet(gn.p1, s1);
- IF Sets.Empty(s1) THEN LL1Error(3, 0) END (*e.g. {ANY} ANY or [ANY] ANY*)
- END;
- gp := gn.next
- END
- END CheckAlternatives;
- BEGIN (* LL1Test *)
- curSy := firstNt; ll1 := TRUE;
- WHILE curSy <= lastNt DO (*for all nonterminals*)
- GetSym(curSy, sn); CheckAlternatives (sn.struct);
- INC (curSy)
- END;
- END LL1Test;
- PROCEDURE TestCompleteness* (VAR ok: BOOLEAN);
- VAR sp: INTEGER; sn: SymbolNode;
- BEGIN
- sp := firstNt; ok := TRUE;
- WHILE sp <= lastNt DO (*for all nonterminals*)
- GetSym (sp, sn);
- IF sn.struct = 0 THEN
- ok := FALSE; NL; Str(" No production for "); Str(sn.name); Texts.Append(Oberon.Log, w.buf)
- END;
- INC(sp)
- END TestCompleteness;
- PROCEDURE TestIfAllNtReached* (VAR ok: BOOLEAN);
- VAR gn: GraphNode; sp: INTEGER; reached: MarkList; sn: SymbolNode;
- PROCEDURE MarkReachedNts (gp: INTEGER);
- VAR gn: GraphNode; sn: SymbolNode;
- BEGIN
- WHILE gp > 0 DO
- GetNode(gp, gn);
- IF gn.typ = nt THEN
- IF ~ Sets.In(reached, gn.p1) THEN (*new nt reached*)
- Sets.Incl(reached, gn.p1);
- GetSym(gn.p1, sn); MarkReachedNts(sn.struct)
- END
- ELSIF gn.typ IN {alt, iter, opt} THEN
- MarkReachedNts(gn.p1);
- IF gn.typ = alt THEN MarkReachedNts(gn.p2) END
- END;
- gp := gn.next
- END
- END MarkReachedNts;
- BEGIN (* TestIfAllNtReached *)
- ClearMarkList(reached);
- GetNode(root, gn); Sets.Incl(reached, gn.p1);
- GetSym(gn.p1, sn); MarkReachedNts(sn.struct);
- sp := firstNt; ok := TRUE;
- WHILE sp <= lastNt DO (*for all nonterminals*)
- IF ~ Sets.In(reached, sp) THEN
- ok := FALSE; GetSym(sp, sn); NL; Str(" "); Str(sn.name); Str(" cannot be reached")
- END;
- INC(sp)
- END;
- Texts.Append(Oberon.Log, w.buf)
- END TestIfAllNtReached;
- PROCEDURE TestIfNtToTerm* (VAR ok: BOOLEAN);
- VAR changed: BOOLEAN; gn: GraphNode; sp: INTEGER;
- sn: SymbolNode;
- termList: MarkList;
- PROCEDURE IsTerm (gp: INTEGER): BOOLEAN;
- VAR gn: GraphNode;
- BEGIN
- WHILE gp > 0 DO
- GetNode(gp, gn);
- IF (gn.typ = nt) & ~ Sets.In(termList, gn.p1)
- OR (gn.typ = alt) & ~ IsTerm(gn.p1) & ~ IsTerm(gn.p2) THEN RETURN FALSE
- END;
- gp := gn.next
- END;
- RETURN TRUE
- END IsTerm;
- BEGIN (* TestIfNtToTerm *)
- ClearMarkList(termList);
- REPEAT
- sp := firstNt; changed := FALSE;
- WHILE sp <= lastNt DO
- IF ~ Sets.In(termList, sp) THEN
- GetSym(sp, sn);
- IF IsTerm(sn.struct) THEN Sets.Incl(termList, sp); changed := TRUE END
- END;
- INC(sp)
- END
- UNTIL ~changed;
- sp := firstNt; ok := TRUE;
- WHILE sp <= lastNt DO
- IF ~ Sets.In(termList, sp) THEN
- ok := FALSE; GetSym(sp, sn); NL; Str(" "); Str(sn.name); Str(" cannot be derived to terminals")
- END;
- INC(sp)
- END;
- Texts.Append(Oberon.Log, w.buf)
- END TestIfNtToTerm;
- PROCEDURE Init*;
- BEGIN
- maxSet := 0; Sets.Clear(set[0]); Sets.Incl(set[0], eofSy);
- firstNt := maxSymbols; maxP := maxSymbols; maxT := -1; maxC := -1;
- lastNt := maxP - 1;
- dummyName := 0;
- nNodes := 0
- END Init;
- BEGIN (* CRT *)
- (* The dummy node gn[0] ensures that none of the procedures
- above have to check for 0 indices. *)
- nNodes := 0;
- gn[0].typ := -1; gn[0].p1 := 0; gn[0].p2 := 0; gn[0].next := 0; gn[0].line := 0;
- Texts.OpenWriter(w)
- END CRT.
-